package com.bigflexlib.utils.priority
{
	import flash.display.DisplayObject;
	import flash.display.DisplayObjectContainer;
	import flash.events.Event;
	import flash.utils.Dictionary;
	
	import com.bigflexlib.core.markers.ISingleton;
	import com.bigflexlib.core.os.thread.Thread;
	
	/**
	 * 
	 * You can associate IPrioritizable elements to certain DisplayObjects
	 * 
	 * TODO: react to display list rearrangements ( reparenting )
	 * TODO: react to visible/invisible and add/remove
	 * 			for now we are only detecting the first added to stage
	 * 
	 * @author aldo.bucchi@gmail.com
	 * 
	 */
	public class DisplayListPriorityManager implements ISingleton
	{

		private var dobj2priority		:Dictionary = new Dictionary( true );
		private var prioritizable2dobj	:Dictionary = new Dictionary( true );

		/**
		 * Sets the priority of a given DObj
		 * 
		 * @param dobj
		 * @param priority
		 * 
		 */
		public function setDisplayObjectPriority( dobj:DisplayObject, priority:uint ):void
		{
			dobj2priority[ dobj ] = priority;
			refreshDown( dobj );		
		}

		/**
		 * Associates a certain prioritizable to a DObj
		 * 
		 * @param query
		 * @param dobj
		 * 
		 */
		public function bindQueryToDisplayObject( prioritizable:IPrioritizable, dobj:DisplayObject ):void
		{
			prioritizable2dobj[ prioritizable ] = dobj;
			prioritizable.priority = getDisplayObjectPriority( dobj );
		
			dobj.addEventListener( Event.ADDED_TO_STAGE, dobj_addedToStage, false, 0, true );
			dobj.addEventListener( Event.REMOVED_FROM_STAGE, dobj_removedFromStage, false, 0, true );
		}

		private function refreshDown( dobj:DisplayObject ):void
		{
			var p:uint = getDisplayObjectPriority( dobj );

			for ( var k:* in prioritizable2dobj )
			{
				var prioritizable:IPrioritizable = k as IPrioritizable;
				if ( prioritizable2dobj[ prioritizable ] == dobj )
					prioritizable.priority = p;
			}
			
			if ( dobj is DisplayObjectContainer )
			{
				var c:DisplayObjectContainer = dobj as DisplayObjectContainer;
				for ( var i:int=0; i<c.numChildren; i++ )
					refreshDown( c.getChildAt( i ) );
			}
		}

		private function getDisplayObjectPriority( dobj:DisplayObject ):uint
		{
			if ( dobj.stage == null ) // this displayobject is not currently added 
				return 0;		

			if ( ! isNaN( dobj2priority[ dobj ] ) )
				return dobj2priority[ dobj ];
				
			if ( dobj.parent )
				return getDisplayObjectPriority( dobj.parent );
				
			return Thread.NORM_PRIORITY;
		}

		private function dobj_addedToStage( event:Event ):void
		{
			refreshDown( event.target as DisplayObject );
		}

		private function dobj_removedFromStage( event:Event ):void
		{
			refreshDown( event.target as DisplayObject );
		}		

		/////////////////////////////////////
		// singleton
		/////////////////////////////////////
		private static var _i:DisplayListPriorityManager;
		public static function get i( ):DisplayListPriorityManager
		{
			if ( _i == null )
				_i = new DisplayListPriorityManager( );
			return _i;
		}
	}
}

